#include "h264Save_gst.hpp"
#include "gst/gstelement.h"
#include <stdexcept>
#include <gst/app/app.h>
#include <sstream>
#include <iostream>

static int object_counter=0;

void enough_data_callback (GstAppSrc * self,gpointer user_data)
{
  std::cout<<"Gstreamer存储队列满了"<<std::endl;
}

H264Save::H264Save(const std::string & savePath)
{
  object_counter++;
  auto CreateNewItemName=[](const std::string& name){
    std::stringstream str;
    str<<name<<"_"<<object_counter;
    return str.str();
  };
  appsrc=gst_element_factory_make("appsrc",CreateNewItemName("appsrc_save").c_str());
  queue=gst_element_factory_make("queue",CreateNewItemName("queue_save").c_str());
  matroskamux=gst_element_factory_make("matroskamux",CreateNewItemName("matroskamux_save").c_str());
  filesink=gst_element_factory_make("filesink",CreateNewItemName("filesink_save").c_str());
  pipeline=gst_pipeline_new(CreateNewItemName("save_pipeline").c_str());

  if(!pipeline || !queue || !filesink || !matroskamux || !appsrc)
    throw std::runtime_error("无法获取所有组建");
  
  //设置元素属性
  g_object_set(appsrc,"do-timestamp",TRUE,nullptr);
  g_object_set(appsrc,"min-percent",0,nullptr);
  g_object_set(appsrc,"format",GST_FORMAT_TIME,nullptr);
  g_object_set(appsrc,"stream-type",0,nullptr);
  g_object_set(matroskamux,"timecodescale",1,nullptr);
  g_object_set(matroskamux,"offset-to-zero",TRUE,nullptr);
  g_object_set(filesink,"location",savePath.c_str(),nullptr);
  g_signal_connect(appsrc,"enough-data",G_CALLBACK(enough_data_callback),(gpointer)this);
  gst_bin_add_many(GST_BIN(pipeline),appsrc,queue,matroskamux,filesink,nullptr);
  GstCaps* caps=gst_caps_new_simple("video/x-h264",
                                    "width",G_TYPE_INT,1920,
                                    "height",G_TYPE_INT,1080,
                                    "framerate",GST_TYPE_FRACTION,30,1,
                                    "stream-format",G_TYPE_STRING,"avc",
                                    nullptr);
  if(!gst_element_link_filtered(appsrc,queue,caps))
    throw std::runtime_error("Can't link gstelement appsrc to queue with current cap");
  gst_caps_unref(caps);

  if(!gst_element_link_many(queue,matroskamux,filesink,nullptr))
    throw std::runtime_error("Can't link gstelement together");

  gst_element_set_state(pipeline,GST_STATE_PLAYING);
}

H264Save::~H264Save()
{
  gst_app_src_end_of_stream((GstAppSrc*)appsrc);
  gst_element_set_state(pipeline,GST_STATE_NULL);
  gst_object_unref(pipeline);
}

void H264Save::InputH264(const char* val,std::size_t size)
{
  //申请一个新的缓存
  GstBuffer* buffer=gst_buffer_new();
  //分配内存
  GstMemory* memory=gst_allocator_alloc(nullptr,size,nullptr);
  //获得数据写入的位置
  GstMapInfo info;
  if(!gst_memory_map(memory,&info,GST_MAP_WRITE))
    throw std::runtime_error("Can't get Gstreamer memory");
  memcpy(info.data,val,size);
  gst_memory_unmap(memory,&info);
  //将memory添加到buffer中
  gst_buffer_append_memory(buffer,memory);
  //发送buffer
  if(gst_app_src_push_buffer((GstAppSrc*)appsrc,buffer)!=GST_FLOW_OK)
    std::cout<<"Can't send buffer to video pipeline"<<std::endl;
}

